SELECT* FROM articles WHERE category_id = ... ORDER BY created DESC LIMIT ...
這樣的查詢很常見,基本上不管什么應用里都能找出一大把類似的SQL來,學院派的讀者看到上面的SQL,可能會說SELECT *不好,應該僅僅查詢需要的字段,那我們就索性徹底點,把SQL改成如下的形式:
SELECT id FROM articles WHERE category_id = ... ORDER BY created DESC LIMIT ...
我們假設這里的id是主鍵,至于文章的具體內容,可以都保存到memcached之類的鍵值類型的緩存里,如此一來,學院派的讀者們應該挑不出什么毛病來了,下面我們就按這條SQL來考慮如何建立索引:
不考慮數據分布之類的特殊情況,任何一個合格的WEB開發人員都知道類似這樣的SQL,應該建立一個”category_id, created“復合索引,但這是最佳答案不?不見得,現在是回頭看看標題的時候了:MySQL里建立索引應該考慮數據庫引擎的類型!
如果我們的數據庫引擎是InnoDB,那么建立”category_id, created“復合索引是最佳答案。讓我們看看InnoDB的索引結構,在InnoDB里,索引結構有一個特殊的地方:非主鍵索引在其BTree的葉節點上會額外保存對應主鍵的值,這樣做一個最直接的好處就是Covering Index,不用再到數據文件里去取id的值,可以直接在索引里得到它。
如果我們的數據庫引擎是MyISAM,那么建立"category_id, created"復合索引就不是最佳答案。因為MyISAM的索引結構里,非主鍵索引并沒有額外保存對應主鍵的值,此時如果想利用上Covering Index,應該建立"category_id, created, id"復合索引。
嘮完了,應該明白我的意思了吧。希望以后大家在考慮索引的時候能思考的更全面一點,實際應用中還有很多類似的問題,比如說多數人在建立索引的時候不從Cardinality(SHOW INDEX FROM ...能看到此參數)的角度看是否合適的問題,Cardinality表示唯一值的個數,一般來說,如果唯一值個數在總行數中所占比例小于20%的話,則可以認為Cardinality太小,此時索引除了拖慢insert/update/delete的速度之外,不會對select產生太大作用;還有一個細節是建立索引的時候未考慮字符集的影響,比如說username字段,如果僅僅允許英文,下劃線之類的符號,那么就不要用gbk,utf-8之類的字符集,而應該使用latin1或者ascii這種簡單的字符集,索引文件會小很多,速度自然就會快很多。這些細節問題需要讀者自己多注意。